// 双锁存高刷芯片

`timescale 1ns/1ps
`default_nettype none

`define GetPwmBits(lsb, num) I_cfg_pwm_setting[lsb+num-1:lsb]

module pixel_display_ex
    #(
    parameter   DW      = 96
    )
    (
    // system signal
    input  wire         I_sclk,  // 125M
    input  wire         I_rst_n,
    // control
    input  wire         I_enable,
    // ext setting
    input  wire         I_ext_lock_output,
    input  wire         I_ext_black_screen,
    // config
    input  wire [9:0]   I_cfg_scan_pixel,   // 一扫的像素数量（不包含虚点）
    input  wire [9:0]   I_cfg_scan_length,  // 一扫的像素数量（包含虚点）
    input  wire [4:0]   I_cfg_scan_max,     // 最大扫描id
    input  wire [4:0]   I_cfg_port_max,     // 最大端口id
    input  wire [7:0]   I_cfg_clock_low,    // 时钟低电平时钟数
    input  wire [7:0]   I_cfg_clock_cycle,  // 时钟整周期时钟数
    input  wire [7:0]   I_cfg_clock_phase,  // 时钟相位
    input  wire         I_cfg_data_polarity, // 数据极性
    input  wire [255:0] I_cfg_pwm_setting,  // 芯片配置
    // cycle info
    output wire         O_cycle_info_rden,
    output wire [8:0]   O_cycle_info_addr,
    input  wire [15:0]  I_cycle_info_q,
    // frame control
    output wire         O_frame_req,         // 请求帧缓冲
    input  wire [1:0]   I_frame_id,          // 帧缓冲id
    // display control
    input  wire         I_display_reset,       // 强制重新开始串移
    output wire         O_display_ready,       // 输出ready
    output wire         O_display_end,         // 每个显示周期结束标识
    output wire         O_display_chain_end,   // 每个串移周期结束标识
    input  wire         I_display_param_en,    // 更新参数
    input  wire [15:0]  I_display_chain_cycle, // 串移时钟数
    input  wire [15:0]  I_display_extra_cycle, // 额外的串移时钟数
    // read sdram
    output wire         O_read_req,         // 读请求
    input  wire         I_read_busy,        // 读忙碌
    output wire [1:0]   O_read_buf_sel,     // 读取SDRAM分块地址
    output wire [3:0]   O_read_bit_sel,     // 读取的bit选择
    output wire [4:0]   O_read_scan_id,     // 读取的scan id
    output wire [4:0]   O_read_port_max,    // 读取的最大port id
    output wire [9:0]   O_read_pixel_count, // 读取的像素数，总是8的倍数，最多512
    output wire         O_read_buf_index,   // 存放到RAM的id
    // scan output
    output wire         O_scan_prep,
    output wire         O_scan_commit,
    output wire [4:0]   O_scan_num,
    // column addr table
    output wire         O_col_addr_req,
    output wire [8:0]   O_col_addr_index,
    input  wire [8:0]   I_col_addr_data,
    // data buffer
    output wire         O_data_ram_rden,
    output wire [9:0]   O_data_ram_addr,
    input  wire [DW-1:0] I_data_ram_q,
    // led signal
    output wire         O_oe_out,
    output wire         O_load_out,
    output wire         O_clock_out,
    output wire [DW-1:0] O_data_out
);
//------------------------Parameter----------------------
// fsm
localparam [2:0]
    IDLE    = 0,
    PREP    = 1,
    REG     = 2,
    DATA    = 3,
    DETECT0 = 4,
    DETECT1 = 5;

//------------------------Local signal-------------------
// fsm
reg  [2:0]  state;
reg  [2:0]  next;

// cycle info
reg  [1:0]  cycle_rden;
reg  [7:0]  cycle_addr;
reg  [4:0]  row_id;
reg         last_row;
reg         last_cycle;
reg  [6:0]  cycle_valid;
reg  [3:0]  high_bit_buf;
reg  [3:0]  low_bit_buf;
reg  [11:0] oe_param0;
reg  [11:0] oe_param1;
reg  [27:0] oe_result;
wire [16:0] oe_tmp;
reg  [15:0] oe_final;
reg  [15:0] high_oe_buf;
reg  [15:0] low_oe_buf;
reg  [1:0]  oe_adj_rden;
reg  [3:0]  oe_adj_addr;
reg  [7:0]  oe_adj_value;

// display control
reg         display_ready;
wire        display_end;
wire        update_param;
reg         display_start;
reg  [15:0] chain_cycle;
reg  [15:0] extra_cycle;
reg  [15:0] chain_cnt;
reg         chain_end;
reg         chain_start;
reg  [15:0] oe_cycle;

// read sdram
reg         read_req;
reg  [3:0]  read_bit_sel;
reg  [4:0]  read_scan_id;
reg         buf_sel;
reg         high_flag; // 是否读取高位数据
reg  [3:0]  high_bit;
reg  [3:0]  low_bit;
reg         last_bit;

// shift request
reg  [15:0] shift2_pos;
reg         shift_start;    // 串移开始
wire        shift_done;
reg         shift_mode;
reg  [3:0]  load_num;
reg         shift_ram_sel;  // 读取ram起始地址
reg  [9:0]  shift_length;   // 串移长度
wire        config_ack;
reg  [DW-1:0] config_data;
reg  [1:0]  reg_id;
reg  [15:0] port0_reg;
reg  [15:0] port1_reg;
reg  [15:0] port2_reg;
reg         bit2_flag;
wire        col_addr_req;
wire [8:0]  col_addr_index;
wire        data_ram_rden;
wire [9:0]  data_ram_addr;
wire        clock_out;
wire [DW-1:0] data_out;
wire        load_out;

// oe control
reg         oe_out;
reg         oe1_flag;
reg         oe1_end;
reg         shift1_end;
reg         bit1_end;
reg  [15:0] oe2_req;
reg  [15:0] oe_cnt;
reg  [15:0] high_oe;
reg  [15:0] low_oe;
wire [15:0] high_oe0; // 5124高位oe前半部分
reg  [15:0] high_oe1; // 5124高位oe后半部分
reg         oe2_flag;
reg         oe2_end;
reg  [9:0]  oe3_req;
reg  [7:0]  load_ext;

// scan decode
reg         scan_prep;
reg         scan_commit;
reg  [4:0]  scan_num;
reg  [15:0] line_pos;

// pwm_setting
wire [1:0]  cfg_reg_num;
wire [3:0]  cfg_reg1_load;
wire [3:0]  cfg_reg2_load;
wire [3:0]  cfg_bit1_load;
wire [3:0]  cfg_bit2_load;
wire [15:0] cfg_port0_reg1;
wire [15:0] cfg_port0_reg2;
wire [15:0] cfg_port1_reg1;
wire [15:0] cfg_port1_reg2;
wire [15:0] cfg_port2_reg1;
wire [15:0] cfg_port2_reg2;
wire [6:0]  cfg_cycle_mult;
wire [15:0] cfg_shift_len;
wire [15:0] cfg_line_pos;
wire [15:0] cfg_blank_cycle;
wire        cfg_5124_mode;
wire        cfg_2017_mode;
wire        cfg_16237_mode;

//od
reg         od_data_sync;
wire        detect_start;
wire        detect_done;
wire        od_scan_prep;
wire        od_scan_commit;
wire [4:0]  od_scan_num;
wire        od_shift_start;
wire        od_shift_mode;
wire [9:0]  od_shift_bit_length;
wire [4:0]  od_shift_load_num;
wire [DW-1:0] od_cfg_data;

//------------------------Instantiation------------------
// data_shift
data_shift /*{{{*/
    #(
    .DW                  (DW      )
    )
  ds (
    .I_sclk              ( I_sclk ),
    .I_rst_n             ( I_rst_n ),
    .I_cfg_clock_low     ( I_cfg_clock_low ),
    .I_cfg_clock_cycle   ( I_cfg_clock_cycle ),
    .I_cfg_clock_phase   ( I_cfg_clock_phase ),
    .I_cfg_data_polarity ( I_cfg_data_polarity ),
    .I_shift_start       ( shift_start ),
    .O_shift_done        ( shift_done ),
    .I_shift_mode        ( shift_mode ),
    .I_load_num          ( load_num ),
    .I_shift_ram_sel     ( shift_ram_sel ),
    .I_shift_length      ( shift_length ),
    .O_col_addr_req      ( col_addr_req ),
    .O_col_addr_index    ( col_addr_index ),
    .I_col_addr_data     ( I_col_addr_data ),
    .O_data_ram_rden     ( data_ram_rden ),
    .O_data_ram_addr     ( data_ram_addr ),
    .I_data_ram_q        ( I_data_ram_q ),
    .O_config_ack        ( config_ack ),
    .I_config_data       ( config_data ),
    .O_shift_clock       ( clock_out ),
    .O_shift_data        ( data_out ),
    .O_shift_load        ( load_out )
);/*}}}*/

//open_detect
open_detect_sm16237 od
(
    .I_sclk(I_sclk),  // 125M
    .I_rst_n(I_rst_n),
    .I_enable(I_enable),
    .I_detect_start(detect_start),
    .O_detect_done(detect_done),
    .I_cfg_scan_max(I_cfg_scan_max),      // 最大扫描id
    .I_cfg_pwm_setting(I_cfg_pwm_setting),   // pwm芯片设置
    .I_cfg_scan_length(I_cfg_scan_length),
    .O_scan_prep(od_scan_prep),
    .O_scan_commit(od_scan_commit),
    .O_scan_num(od_scan_num),
    .O_shift_start(od_shift_start),
    .O_shift_mode(od_shift_mode),
    .I_shift_done(shift_done),
    .O_shift_bit_length(od_shift_bit_length),
    .O_shift_load_num(od_shift_load_num),
    .O_cfg_data(od_cfg_data),
    .I_cfg_ack(config_ack)
);

//------------------------Body---------------------------
//{{{+++++++++++++++++++++fsm++++++++++++++++++++++++++++
// state
always @(posedge I_sclk or negedge I_rst_n) begin
    if (~I_rst_n)
        state <= IDLE;
    else if (I_display_reset || !I_enable) // 强制同步
        state <= IDLE;
    else
        state <= next;
end

// next
always @(*) begin
    case (state)
        IDLE: begin
            if(!display_ready && cfg_port0_reg1[15] && cfg_16237_mode)
                next = DETECT0;
            else
                next = PREP;
        end

        DETECT0: begin
            next = DETECT1;
        end

        DETECT1: begin
            if(detect_done)
                next = PREP;
            else
                next = DETECT1;
        end

        PREP: begin
            next = REG;
        end

        REG: begin
            if (~chain_end)
                next = REG;
            else
                next = DATA;
        end

        DATA: begin
            if (~chain_end)
                next = DATA;
            else if (display_end)
                next = REG;
            else
                next = DATA;
        end
        
        default: begin
            next = IDLE;
        end
    endcase
end
//}}}++++++++++++++++++++++++++++++++++++++++++++++++++++

//{{{+++++++++++++++++++++cycle info+++++++++++++++++++++
assign O_cycle_info_rden = cycle_rden[0] | oe_adj_rden[0];
assign O_cycle_info_addr = oe_adj_rden[0]? {1'b1, 4'd0, oe_adj_addr} : cycle_addr;

// cycle_rden
always @(posedge I_sclk or negedge I_rst_n) begin
    if (!I_rst_n)
        cycle_rden <= 1'b0;
    else if (!I_enable)
        cycle_rden <= 1'b0;
    else if (chain_start && row_id == 1'b0 && !last_cycle)
        cycle_rden <= 2'b11;
    else
        cycle_rden <= cycle_rden >> 1;
end

// cycle_addr
always @(posedge I_sclk or negedge I_rst_n) begin
    if (~I_rst_n)
        cycle_addr <= 1'b0;
    else if (!I_enable)
        cycle_addr <= 1'b0;
    else if (state == PREP || display_end)
        cycle_addr <= 1'b0;
    else if (cycle_rden[0])
        cycle_addr <= cycle_addr + 1'b1;
end

// oe_adj_rden
always @(posedge I_sclk or negedge I_rst_n) begin
    if (!I_rst_n)
        oe_adj_rden <= 1'b0;
    else if (!I_enable)
        oe_adj_rden <= 1'b0;
    else
        oe_adj_rden <= {cycle_rden[0], oe_adj_rden[1]};
end

// oe_adj_addr
always @(posedge I_sclk or negedge I_rst_n) begin
    if (!I_rst_n)
        oe_adj_addr <= 1'b0;
    else if (cycle_valid[1] || cycle_valid[2])
        oe_adj_addr <= I_cycle_info_q[3:0];
end

// oe_adj_value
always @(posedge I_sclk or negedge I_rst_n) begin
    if (!I_rst_n)
        oe_adj_value <= 1'b0;
    else if (cycle_valid[3] || cycle_valid[4]) begin
      //if (I_cycle_info_q[15:8] == 8'hff)
        if (I_cycle_info_q[15] == 1)
            oe_adj_value <= 1'b0;
        else
            oe_adj_value <= I_cycle_info_q[7:0];
    end
end

// row_id
always @(posedge I_sclk or negedge I_rst_n) begin
    if (!I_rst_n)
        row_id <= 1'b0;
    else if (state == PREP || display_end)
        row_id <= 1'b0;
    else if (state == DATA && chain_end) begin
        if (last_row)
            row_id <= 1'b0;
        else
            row_id <= row_id + 1'b1;
    end
end

// last_row
always @(posedge I_sclk or negedge I_rst_n) begin
    if (!I_rst_n)
        last_row <= 1'b0;
    else if (state == REG)
        last_row <= 1'b0;
    else if (state == DATA && chain_end) begin
        if (row_id == I_cfg_scan_max - 1'b1)
            last_row <= 1'b1;
        else
            last_row <= 1'b0;
    end
end

// last_cycle
always @(posedge I_sclk or negedge I_rst_n) begin
    if (!I_rst_n)
        last_cycle <= 1'b0;
    else if (state == PREP || display_end)
        last_cycle <= 1'b0;
    else if (state == DATA && last_row && chain_end && cycle_addr == {cfg_cycle_mult, 1'b0})
        last_cycle <= 1'b1;
end

// cycle_valid
always @(posedge I_sclk or negedge I_rst_n) begin
    if (!I_rst_n)
        cycle_valid <= 1'b0;
    else if (state == PREP || display_end)
        cycle_valid <= 1'b1;
    else if (chain_start && row_id == 1'b0 && !last_cycle)
        cycle_valid <= 1'b1;
    else
        cycle_valid <= cycle_valid << 1;
end

// high_bit_buf
always @(posedge I_sclk or negedge I_rst_n) begin
    if (!I_rst_n)
        high_bit_buf <= 1'b0;
    else if (cycle_valid[1])
        high_bit_buf <= I_cycle_info_q[3:0];
end

// low_bit_buf
always @(posedge I_sclk or negedge I_rst_n) begin
    if (!I_rst_n)
        low_bit_buf <= 1'b0;
    else if (cycle_valid[2])
        low_bit_buf <= I_cycle_info_q[3:0];
end

// oe_param0
always @(posedge I_sclk or negedge I_rst_n) begin
    if (!I_rst_n)
        oe_param0 <= 1'b0;
    else if (cycle_valid[1] || cycle_valid[2])
        oe_param0 <= I_cycle_info_q[15:4];
end

// oe_param1
always @(posedge I_sclk or negedge I_rst_n) begin
    if (!I_rst_n)
        oe_param1 <= 1'b0;
    else if (cycle_valid[2] || cycle_valid[3])
        oe_param1 <= oe_param0;
end

// oe_result
always @(posedge I_sclk or negedge I_rst_n) begin
    if (!I_rst_n)
        oe_result <= 1'b0;
    else if (cycle_valid[3] || cycle_valid[4])
        oe_result <= oe_param1 * oe_cycle;
end

assign oe_tmp = oe_result[27:12] + {{10{oe_adj_value[7]}}, oe_adj_value[6:0]};

// oe_final
always @(posedge I_sclk or negedge I_rst_n) begin
    if (!I_rst_n)
        oe_final <= 1'b0;
    else if (oe_tmp[16] == 1'b0 && oe_tmp[15:0] != 1'b0) // 未溢出
        oe_final <= oe_tmp[15:0];
    else if (oe_adj_value[7]) // 向下溢出
        oe_final <= 1'b1;
    else // 向上溢出
        oe_final <= 16'hffff;
end

// high_oe_buf
always @(posedge I_sclk or negedge I_rst_n) begin
    if (!I_rst_n)
        high_oe_buf <= 1'b0;
    else if (cycle_valid[5])
        high_oe_buf <= oe_final;
end

// low_oe_buf
always @(posedge I_sclk or negedge I_rst_n) begin
    if (!I_rst_n)
        low_oe_buf <= 1'b0;
    else if (cycle_valid[6])
        low_oe_buf <= oe_final;
end
//}}}++++++++++++++++++++++++++++++++++++++++++++++++++++

//{{{+++++++++++++++++++++frame control++++++++++++++++++
assign O_frame_req = I_enable && !I_ext_lock_output && display_start;
//}}}++++++++++++++++++++++++++++++++++++++++++++++++++++

//{{{+++++++++++++++++++++display control++++++++++++++++
assign O_display_end = display_end;
assign O_display_chain_end = chain_end;

assign display_end  = last_row && last_cycle && chain_end;
assign update_param = (state == PREP) || (display_end && I_display_param_en);

// display_start
always @(posedge I_sclk or negedge I_rst_n) begin
    if (~I_rst_n)
        display_start <= 1'b0;
    else if (state == PREP)
        display_start <= 1'b1;
    else if (display_end)
        display_start <= 1'b1;
    else
        display_start <= 1'b0;
end

// chain_cycle
always @(posedge I_sclk or negedge I_rst_n) begin
    if (~I_rst_n)
        chain_cycle <= 1'b0;
    else if (update_param)
        chain_cycle <= I_display_chain_cycle;
end

// extra_cycle
always @(posedge I_sclk or negedge I_rst_n) begin
    if (~I_rst_n)
        extra_cycle <= 1'b0;
    else if (update_param)
        extra_cycle <= I_display_extra_cycle;
    else if (chain_end && extra_cycle > 1'b0)
        extra_cycle <= extra_cycle - 1'b1;
end

// oe_cycle
always @(posedge I_sclk or negedge I_rst_n) begin
    if (~I_rst_n)
        oe_cycle <= 1'b0;
    else if (update_param)
        oe_cycle <= I_display_chain_cycle - cfg_blank_cycle;
end

// chain_cnt
always @(posedge I_sclk or negedge I_rst_n) begin
    if (~I_rst_n)
        chain_cnt <= 1'b1;
    else if (state == IDLE || state == PREP)
        chain_cnt <= 1'b1;
    else if (chain_end) begin
        if (extra_cycle > 1'b0)
            chain_cnt <= 1'b0;
        else
            chain_cnt <= 1'b1;
    end
    else
        chain_cnt <= chain_cnt + 1'b1;
end

// chain_end
always @(posedge I_sclk or negedge I_rst_n) begin
    if (~I_rst_n)
        chain_end <= 1'b0;
    else if (!I_enable)
        chain_end <= 1'b0;
    else
        chain_end <= (chain_cnt == chain_cycle - 1'b1);
end

// chain_start
always @(posedge I_sclk or negedge I_rst_n) begin
    if (!I_rst_n)
        chain_start <= 1'b0;
    else if (state == PREP)
        chain_start <= 1'b1;
    else if (chain_end)
        chain_start <= 1'b1;
    else
        chain_start <= 1'b0;
end
//}}}++++++++++++++++++++++++++++++++++++++++++++++++++++

//{{{+++++++++++++++++++++read sdram+++++++++++++++++++++
assign O_read_req         = read_req;
assign O_read_buf_sel     = (!I_enable)? 1'b0 : I_frame_id;
assign O_read_bit_sel     = read_bit_sel;
assign O_read_scan_id     = read_scan_id;
assign O_read_port_max    = (!I_enable)? 1'b0 : I_cfg_port_max;
assign O_read_pixel_count = (!I_enable)? 1'b0 : I_cfg_scan_pixel;
assign O_read_buf_index   = (!I_enable)? 1'b0 : buf_sel;

// read_req
always @(posedge I_sclk or negedge I_rst_n) begin
    if (!I_rst_n)
        read_req <= 1'b0;
    else if (!I_enable)
        read_req <= 1'b0;
    else if (state == REG && (chain_cnt == 4'd10 || chain_cnt == chain_cycle[15:1]))
        read_req <= 1'b1;
    else if (state == DATA && !last_bit && (chain_cnt == 4'd10 || chain_cnt == chain_cycle[15:1]))
        read_req <= 1'b1;
    else
        read_req <= 1'b0;
end

// read_bit_sel
always @(posedge I_sclk or negedge I_rst_n) begin
    if (!I_rst_n)
        read_bit_sel <= 1'b0;
    else if (!I_enable)
        read_bit_sel <= 1'b0;
    else if (high_flag)
        read_bit_sel <= high_bit;
    else
        read_bit_sel <= low_bit;
end

// read_scan_id
always @(posedge I_sclk or negedge I_rst_n) begin
    if (!I_rst_n)
        read_scan_id <= 1'b0;
    else if (!I_enable)
        read_scan_id <= 1'b0;
    else if (state == PREP || display_end)
        read_scan_id <= 1'b0;
    else if (read_req && !high_flag) begin
        if (read_scan_id == I_cfg_scan_max)
            read_scan_id <= 1'b0;
        else
            read_scan_id <= read_scan_id + 1'b1;
    end
end

// buf_sel
always @(posedge I_sclk or negedge I_rst_n) begin
    if (~I_rst_n)
        buf_sel <= 1'b0;
    else if (state == PREP || display_end)
        buf_sel <= 1'b0;
    else if (read_req)
        buf_sel <= ~buf_sel;
end

// high_flag
always @(posedge I_sclk or negedge I_rst_n) begin
    if (!I_rst_n)
        high_flag <= 1'b1;
    else if (state == PREP || display_end)
        high_flag <= 1'b1;
    else if (read_req)
        high_flag <= !high_flag;
end

// high_bit
always @(posedge I_sclk or negedge I_rst_n) begin
    if (!I_rst_n)
        high_bit <= 1'b0;
    else if (state == REG && chain_cnt == 4'd8)
        high_bit <= high_bit_buf;
    else if (state == DATA && chain_end && row_id == I_cfg_scan_max - 1'b1)
        high_bit <= high_bit_buf;
end

// low_bit
always @(posedge I_sclk or negedge I_rst_n) begin
    if (!I_rst_n)
        low_bit <= 1'b0;
    else if (state == REG && chain_cnt == 4'd8)
        low_bit <= low_bit_buf;
    else if (state == DATA && chain_end && row_id == I_cfg_scan_max - 1'b1)
        low_bit <= low_bit_buf;
end

// last_bit
always @(posedge I_sclk or negedge I_rst_n) begin
    if (!I_rst_n)
        last_bit <= 1'b0;
    else if (state == REG)
        last_bit <= 1'b0;
    else if (chain_start && last_row && last_cycle)
        last_bit <= 1'b1;
end
//}}}++++++++++++++++++++++++++++++++++++++++++++++++++++

//{{{+++++++++++++++++++++data shift+++++++++++++++++++++
//assign shift_length  = (od_data_sync) ? od_shift_bit_length : I_cfg_scan_length;
//assign config_data   = (od_data_sync) ? od_cfg_data         : {24{port2_reg[15], port1_reg[15], port0_reg[15]}};

assign O_col_addr_req   = (!I_enable)? 1'b0 : col_addr_req;
assign O_col_addr_index = (!I_enable)? 1'b0 : col_addr_index;
assign O_data_ram_rden  = (!I_enable)? 1'b0 : data_ram_rden;
assign O_data_ram_addr  = (!I_enable)? 1'b0 : data_ram_addr;
assign O_clock_out      = (!I_enable)? 1'b0 : (clock_out | (|oe3_req[5:2])| (|oe2_req[7:4]) | (|load_ext[3:0]));
assign O_data_out       = (!I_enable)? 1'b0 : data_out;
assign O_load_out       = (!I_enable)? 1'b0 : (load_out | (|oe3_req[9:2]) | (|oe2_req[11:4]) | (|load_ext[7:0]));

//data sync
/*always @(posedge I_sclk or negedge I_rst_n) begin
    if (!I_rst_n)
        od_data_sync <= 1'b0;
    else if(state == DETECT1)
        od_data_sync <= 1'b1;
    else
        od_data_sync <= 1'b0;
end*/

//shift_length
always @(posedge I_sclk or negedge I_rst_n) begin
    if(!I_rst_n)
        shift_length <= 1'b0;
    else if (state == DETECT1)
        shift_length <= od_shift_bit_length;
    else
        shift_length <= I_cfg_scan_length;
end

//config_data
always @(posedge I_sclk or negedge I_rst_n) begin
    if(!I_rst_n)
        config_data <= 1'b0;
    else if (state == DETECT1)
        config_data <= od_cfg_data;
    else
        config_data <= {(DW/3){port2_reg[15], port1_reg[15], port0_reg[15]}};
end

// shift2_pos
always @(posedge I_sclk or negedge I_rst_n) begin
    if (!I_rst_n)
        shift2_pos <= 1'b0;
    else if (state == REG && chain_start) begin
        if (cfg_2017_mode)
            shift2_pos <= chain_cycle - cfg_shift_len - 4'd8;
        else
            shift2_pos <= chain_cycle - cfg_shift_len;
    end
end

// shift_start
always @(posedge I_sclk or negedge I_rst_n) begin
    if (!I_rst_n)
        shift_start <= 1'b0;
    else if (state == DETECT1)
        shift_start <= od_shift_start;
    else if (chain_start)
        shift_start <= 1'b1;
    else if (chain_cnt == shift2_pos)
        shift_start <= 1'b1;
    else
        shift_start <= 1'b0;
end

// shift_mode
always @(posedge I_sclk or negedge I_rst_n) begin
    if (!I_rst_n)
        shift_mode <= 1'b0;
    else if (state == DETECT1)
        shift_mode <= od_shift_mode;
    else if (state == PREP)
        shift_mode <= 1'b0;
    else if (state == DATA && last_row && last_cycle && shift_start)
        shift_mode <= 1'b0;
    else if (state == REG && shift_start)
        shift_mode <= 1'b1;
end

// shift_ram_sel
always @(posedge I_sclk or negedge I_rst_n) begin
    if (!I_rst_n)
        shift_ram_sel <= 1'b0;
    else if (state == PREP || display_end)
        shift_ram_sel <= 1'b0;
    else if (shift_start && shift_mode == 1'b1)
        shift_ram_sel <= ~shift_ram_sel;
end

// load_num
always @(posedge I_sclk or negedge I_rst_n) begin
    if (!I_rst_n)
        load_num <= 1'b0;
    else if (state == DETECT1) 
        load_num <= od_shift_load_num;
    else if (shift_mode == 1'b0) begin
        if (reg_id == 1'b1)
            load_num <= cfg_reg1_load;
        else
            load_num <= cfg_reg2_load;
    end
    else if (cfg_5124_mode) begin
        if (state == DATA && chain_start)
            load_num <= 1'b1;
        else
            load_num <= 1'b0;
    end
    else begin
        if (state == DATA && chain_start && row_id == 1'b0)
            load_num <= cfg_bit2_load;
        else
            load_num <= cfg_bit1_load;
    end
end

// reg_id
always @(posedge I_sclk or negedge I_rst_n) begin
    if (!I_rst_n)
        reg_id <= 1'b1;
    else if (state == IDLE)
        reg_id <= 1'b1;
    else if (shift_start && shift_mode == 1'b0) begin
        if (reg_id == cfg_reg_num)
            reg_id <= 1'b1;
        else
            reg_id <= reg_id + 1'b1;
    end
end

// port0_reg
always @(posedge I_sclk or negedge I_rst_n) begin
    if (!I_rst_n)
        port0_reg <= 1'b0;
    else if (shift_start && shift_mode == 1'b0) begin
        if (reg_id == 1'b1)
            port0_reg <= cfg_port0_reg1;
        else
            port0_reg <= cfg_port0_reg2;
    end
    else if (config_ack)
        port0_reg <= {port0_reg[14:0], port0_reg[15]};
end

// port1_reg
always @(posedge I_sclk or negedge I_rst_n) begin
    if (!I_rst_n)
        port1_reg <= 1'b0;
    else if (shift_start && shift_mode == 1'b0) begin
        if (reg_id == 1'b1)
            port1_reg <= cfg_port1_reg1;
        else
            port1_reg <= cfg_port1_reg2;
    end
    else if (config_ack)
        port1_reg <= {port1_reg[14:0], port1_reg[15]};
end

// port2_reg
always @(posedge I_sclk or negedge I_rst_n) begin
    if (!I_rst_n)
        port2_reg <= 1'b0;
    else if (shift_start && shift_mode == 1'b0) begin
        if (reg_id == 1'b1)
            port2_reg <= cfg_port2_reg1;
        else
            port2_reg <= cfg_port2_reg2;
    end
    else if (config_ack)
        port2_reg <= {port2_reg[14:0], port2_reg[15]};
end
//}}}++++++++++++++++++++++++++++++++++++++++++++++++++++

//{{{+++++++++++++++++++++oe control+++++++++++++++++++++
assign O_oe_out = oe_out;

// oe_out
always @(posedge I_sclk or negedge I_rst_n) begin
    if (!I_rst_n)
        oe_out <= 1'b0;
    else if (!I_enable)
        oe_out <= 1'b0;
    else if (I_ext_black_screen)
        oe_out <= 1'b0;
    else if (state == PREP || display_end)
        oe_out <= 1'b0;
    else if (state == DATA && chain_start || oe2_req[0] || oe3_req[0])
        oe_out <= 1'b1;
    else if (oe_cnt == 1'b1)
        oe_out <= 1'b0;
end

// oe1_flag
always @(posedge I_sclk or negedge I_rst_n) begin
    if (!I_rst_n)
        oe1_flag <= 1'b1;
    else if (chain_end)
        oe1_flag <= 1'b1;
    else if (oe_cnt == 1'b1)
        oe1_flag <= 1'b0;
end

// oe1_end
always @(posedge I_sclk or negedge I_rst_n) begin
    if (!I_rst_n)
        oe1_end <= 1'b0;
    else if (oe1_flag && oe_cnt == 1'b1)
        oe1_end <= 1'b1;
    else if (chain_end || bit1_end)
        oe1_end <= 1'b0;
end

// shift1_end
always @(posedge I_sclk or negedge I_rst_n) begin
    if (!I_rst_n)
        shift1_end <= 1'b0;
    else if (chain_end)
        shift1_end <= 1'b0;
    else if (shift_done)
        shift1_end <= 1'b1;
end

// bit1_end
always @(posedge I_sclk or negedge I_rst_n) begin
    if (!I_rst_n)
        bit1_end <= 1'b0;
    else
        bit1_end <= shift1_end && oe1_end;
end

// oe2_req
always @(posedge I_sclk or negedge I_rst_n) begin
    if (!I_rst_n)
        oe2_req <= 1'b0;
    else if (bit1_end) begin
        if (cfg_2017_mode)
            oe2_req <= 16'h8000;
        else
            oe2_req <= 4'b1000;
    end
    else
        oe2_req <= oe2_req >> 1;
end

// oe2_flag
always @(posedge I_sclk or negedge I_rst_n) begin
    if (!I_rst_n)
        oe2_flag <= 1'b0;
    else if (bit1_end)
        oe2_flag <= 1'b1;
    else if (oe_cnt == 1'b1)
        oe2_flag <= 1'b0;
end

// oe2_end
always @(posedge I_sclk or negedge I_rst_n) begin
    if (!I_rst_n)
        oe2_end <= 1'b0;
    else if (oe2_flag && oe_cnt == 1'b1)
        oe2_end <= 1'b1;
    else
        oe2_end <= 1'b0;
end

// oe3_req
always @(posedge I_sclk or negedge I_rst_n) begin
    if (!I_rst_n)
        oe3_req <= 1'b0;
    else if (cfg_5124_mode && oe2_end && high_oe1 > 1'b0)
        oe3_req <= 10'h200;
    else
        oe3_req <= oe3_req >> 1;
end

// oe_cnt
always @(posedge I_sclk or negedge I_rst_n) begin
    if (!I_rst_n)
        oe_cnt <= 1'b0;
    else if (state == DATA && chain_start) begin
        if (cfg_5124_mode && high_oe > high_oe0)
            oe_cnt <= high_oe0;
        else
            oe_cnt <= high_oe;
    end
    else if (oe2_req[0])
        oe_cnt <= low_oe;
    else if (oe3_req[0])
        oe_cnt <= high_oe1;
    else if (oe_cnt > 1'b0)
        oe_cnt <= oe_cnt - 1'b1;
end

// high_oe
always @(posedge I_sclk or negedge I_rst_n) begin
    if (!I_rst_n)
        high_oe <= 1'b1;
    else if (chain_end && (state == REG || state == DATA && last_row))
        high_oe <= high_oe_buf;
end

// low_oe
always @(posedge I_sclk or negedge I_rst_n) begin
    if (!I_rst_n)
        low_oe <= 1'b1;
    else if (chain_end && (state == REG || state == DATA && last_row))
        low_oe <= low_oe_buf;
end

assign high_oe0 = cfg_shift_len - 6'd32;

// high_oe1
always @(posedge I_sclk or negedge I_rst_n) begin
    if (!I_rst_n)
        high_oe1 <= 1'b0;
    else if (state == DATA && chain_start) begin
        if (cfg_5124_mode && high_oe > high_oe0)
            high_oe1 <= high_oe - high_oe0;
        else
            high_oe1 <= 1'b0;
    end
end

// load_ext
always @(posedge I_sclk or negedge I_rst_n) begin
    if (~I_rst_n)
        load_ext <= 1'b0;
    else if (cfg_2017_mode && !(last_cycle && last_row) && chain_cnt == chain_cycle - 4'd8)
        load_ext <= 8'h80;
    else
        load_ext <= load_ext >> 1'b1;
end
//}}}++++++++++++++++++++++++++++++++++++++++++++++++++++

//{{{+++++++++++++++++++++scan decode++++++++++++++++++++
assign O_scan_prep   = scan_prep;
assign O_scan_num    = scan_num;
assign O_scan_commit = scan_commit;

// line_pos
always @(posedge I_sclk or negedge I_rst_n) begin
    if (!I_rst_n)
        line_pos <= 1'b0;
    else if (state == REG && chain_start)
        line_pos <= chain_cycle - cfg_line_pos;
end

// scan_prep
always @(posedge I_sclk or negedge I_rst_n) begin
    if (!I_rst_n)
        scan_prep <= 1'b0;
    else if (!I_enable)
        scan_prep <= 1'b0;
    else if (state == DETECT1)
        scan_prep <= od_scan_prep;
    else if (state == DATA && chain_start)
        scan_prep <= 1'b1;
    else
        scan_prep <= 1'b0;
end

// scan_commit
always @(posedge I_sclk or negedge I_rst_n) begin
    if (!I_rst_n)
        scan_commit <= 1'b0;
    else if (!I_enable)
        scan_commit <= 1'b0;
    else if (state == DETECT1)
        scan_commit <= od_scan_commit;
    else if (state == DATA && chain_cnt == line_pos)
        scan_commit <= 1'b1;
    else
        scan_commit <= 1'b0;
end

// scan_num
always @(posedge I_sclk or negedge I_rst_n) begin
    if (!I_rst_n)
        scan_num <= 1'b0;
    else if (!I_enable)
        scan_num <= 1'b0;
    else if (state == DETECT1)
        scan_num <= od_scan_num;
    else if (last_row)
        scan_num <= 1'b0;
    else
        scan_num <= row_id + 1'b1;
end
//}}}++++++++++++++++++++++++++++++++++++++++++++++++++++

//{{{+++++++++++++++++++++pwm setting++++++++++++++++++++
assign cfg_reg_num     = `GetPwmBits(0, 2);
assign cfg_reg1_load   = `GetPwmBits(8, 4);
assign cfg_reg2_load   = `GetPwmBits(16, 4);
assign cfg_bit1_load   = `GetPwmBits(24, 4);
assign cfg_bit2_load   = `GetPwmBits(32, 4);
assign cfg_port0_reg1  = `GetPwmBits(40, 16);
assign cfg_port0_reg2  = `GetPwmBits(56, 16);
assign cfg_port1_reg1  = `GetPwmBits(72, 16);
assign cfg_port1_reg2  = `GetPwmBits(88, 16);
assign cfg_port2_reg1  = `GetPwmBits(104, 16);
assign cfg_port2_reg2  = `GetPwmBits(120, 16);
assign cfg_cycle_mult  = `GetPwmBits(136, 7);
assign cfg_shift_len   = `GetPwmBits(144, 16);
assign cfg_line_pos    = `GetPwmBits(160, 16);
assign cfg_blank_cycle = `GetPwmBits(176, 16);
assign cfg_5124_mode   = `GetPwmBits(192, 1);
assign cfg_2017_mode   = `GetPwmBits(193, 1);
assign cfg_16237_mode  = `GetPwmBits(194, 1);
//}}}++++++++++++++++++++++++++++++++++++++++++++++++++++

assign O_display_ready = display_ready;
//display_ready
always @(posedge I_sclk or negedge I_rst_n) begin
    if(!I_rst_n)
        display_ready <= 1'b0;
    else if(!I_enable)
        display_ready <= 1'b0;
    else if(state == PREP)
        display_ready <= 1'b1;
end

//detect_start
assign detect_start = (state == DETECT0);

endmodule

`default_nettype wire

// vim:set ts=4 sw=4 et fenc=utf-8 fdm=marker:
